#pragma once
#include "../common/net.hpp"
#include "../common/message.hpp"
#include <set>

namespace MyRpc
{
    namespace server
    {
        class ProviderManage
        {
        public:
            using ptr = std::shared_ptr<ProviderManage>;
            struct Provider  // 对每一个服务提供者的描述
            {
                using ptr = std::shared_ptr<Provider>;
                std::mutex mtx;
                std::vector<std::string> methods;  // 提供的服务方法名
                BaseConnection::ptr conn;
                Addr host;   // 提供服务的主机
                Provider(const BaseConnection::ptr& c, const Addr& h)
                    :conn(c)
                    ,host(h)
                {}
                void appendMethod(const std::string& method)
                {
                    std::lock_guard<std::mutex> lock(mtx);
                    methods.push_back(method);
                }
            };
            
            // 添加服务提供着
            Provider::ptr addProvider(const BaseConnection::ptr& c, const Addr& h, const std::string& method)
            {
                Provider::ptr provide;
                // 先判断是否有这个服务
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    auto it = _conns.find(c);
                    if(it == _conns.end())
                    {
                        // 没有找到这个连接 先建立新的连接
                        provide = std::make_shared<Provider>(c, h);
                        _conns[c] = provide;
                    }
                    else
                    {
                        // 找到了这个服务的连接了
                        // 现在只需要把这个服务插入到服务提供者的hash中即可
                        provide = it->second;
                    }
                    auto& providers = _providers[method];
                    providers.insert(provide);
                }
                provide->appendMethod(method);
                return provide;
            }
            // 删除服务提供描述
            void delProvider(const BaseConnection::ptr& c)
            {
                std::lock_guard<std::mutex> lock(_mtx);
                auto it = _conns.find(c);
                if(it == _conns.end())
                {
                    // 没有找到 无法删除
                    return;
                }
                // 走到这块就可以删除 先删除提供者管理信息里面的方法管理者
                for(auto& e : it->second->methods)  // 遍历这个服务管理者所提供的方法 
                {
                    _providers[e].erase(it->second);  // 删除管理服务和对应服务者的提供信息
                }
                // 最后再删除这个连接管理
                _conns.erase(c);
            }
            // 获取服务提供描述
            Provider::ptr selectProvider(const BaseConnection::ptr& c)
            {
                std::lock_guard<std::mutex> lock(_mtx);
                auto it = _conns.find(c);
                if(it == _conns.end())
                {
                    return Provider::ptr();
                }
                // 走到这块就是找到了
                return it->second;
            }
            
            // 获取存在这个方法的所有主机
            std::vector<Addr> getHosts(const std::string& method)
            {
                std::lock_guard<std::mutex> lock(_mtx);
                auto it = _providers.find(method);
                if(it == _providers.end())
                {
                    // 没有找到这个方法
                    return std::vector<Addr>();
                }
                // 走到这块就是找到了
                std::vector<Addr> hosts;
                for(auto& provider : it->second)
                {
                    // 添加可以提供服务的主机地址
                    hosts.push_back(provider->host);
                }
                return hosts;
            }

        private:
            std::mutex _mtx;
            std::unordered_map<std::string, std::set<Provider::ptr>> _providers;  // 对应的服务和服务的提供者信息
            std::unordered_map<BaseConnection::ptr, Provider::ptr> _conns;   // 这个是储存每一个提供服务的连接
        };

        class DiscoveryManage
        {
        public:
            using ptr = std::shared_ptr<DiscoveryManage>;
            struct Discovery
            {
                using ptr = std::shared_ptr<Discovery>;
                std::mutex _mtx;
                BaseConnection::ptr conn;
                std::vector<std::string> methods;
                Discovery(const BaseConnection::ptr& c)
                    :conn(c)
                {}
                void appendMethod(const std::string& method)
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    methods.push_back(method);
                }
            };
            // 添加某种服务的发现者信息
            Discovery::ptr addDiscovery(const BaseConnection::ptr& c, const std::string& method)
            {
                Discovery::ptr discovery;
                {
                    // 先判断是否有这个服务
                    std::lock_guard<std::mutex> lock(_mtx);
                    auto it = _conns.find(c);
                    if(it == _conns.end()) // 没有找到这个服务发现者
                    {
                        discovery = std::make_shared<Discovery>(c);
                        // 把这个服务关心的对象添加进去
                        _discovers[method].insert(discovery);
                    }
                    else
                    {
                        discovery = it->second;
                    }
                }
                discovery->appendMethod(method);
                return discovery;
            }
            void delDiscovery(const BaseConnection::ptr& c)
            {
                // 先判断是否有这个服务
                std::lock_guard<std::mutex> lock(_mtx);
                auto it = _conns.find(c);
                if(it == _conns.end())
                {
                    // 没有找到这个服务发现者信息
                    return;
                }
                // 走到这块就是找到啦
                for(auto& e : it->second->methods)
                {
                    // 删除它所有的服务发现
                    _discovers[e].erase(it->second);
                }
                _conns.erase(c);
            }
            // 给这个方法对应的发现者发送上线通知
            void onNotify(const std::string& method, const Addr& addr)
            {
                return notify(method, addr, ServiceRequestOpType::SERVICE_ONLINE);
            }
            // 给这个方法对应的发现者发送下线通知
            void offNotify(const std::string& method, const Addr& addr)
            {
                return notify(method, addr, ServiceRequestOpType::SERVICE_OFFLINE);
            }
        private:
            void notify(const std::string& method, const Addr& addr, ServiceRequestOpType opty)
            {
                std::lock_guard<std::mutex> lock(_mtx);
                // 先查找这个方法是否存在
                auto it = _discovers.find(method);
                if(it == _discovers.end())
                {
                    // 没有找到这个方法
                    return;
                }
                // 构造发送信息
                BaseMessage::ptr msg = MessageFactory::create(MType::REQ_SERVICE);
                auto rpc_msg = std::dynamic_pointer_cast<ServiceRequest>(msg);
                rpc_msg->setMethod(method);
                rpc_msg->setHost(addr);
                rpc_msg->setId(UUID::uuid());
                rpc_msg->setMType(MType::RSP_SERVICE);
                rpc_msg->setOptype(opty);
                // 找到了这个方法 准备发送
                for(auto& e : _discovers[method])
                {
                    // 找到这个方法所对应的所有发现者 然后给他们发送上线通知
                    e->conn->send(msg);
                }
            }
        private:
            std::mutex _mtx;
            std::unordered_map<std::string, std::set<Discovery::ptr>> _discovers;  // 对应的服务和服务的发现者信息
            std::unordered_map<BaseConnection::ptr, Discovery::ptr> _conns;   // 这个是储存每一个发现的连接
        };

        // 对provider和discovery的管理
        class ProviderDiscoveryManage
        {
        public:
            using ptr = std::shared_ptr<ProviderDiscoveryManage>;
            ProviderDiscoveryManage()
                :_provide_manage(std::make_shared<ProviderManage>())
                ,_discovery_manage(std::make_shared<DiscoveryManage>())
            {}

            // 这个接口是注册给dispatcher模块的 收到响应之后我们应该做回应给客户端
            void onServiceRequest(const BaseConnection::ptr& conn, const ServiceRequest::ptr& msg)
            {
                // 通过发送过来的请求判断是需要服务注册还是服务发现服务
                ServiceRequestOpType opty = msg->optype();
                if(opty == ServiceRequestOpType::SERVICE_REGISTRY)
                {
                    LOG(DEBUGLEVEL, "进入到注册方法中!");
                    ProviderManage::Provider::ptr provider;
                    // 请求服务注册服务 创建一个provider的方法提供者对象 进行方法上线的通知
                    _provide_manage->addProvider(conn, msg->host(), msg->method());
                    // 进行服务上线通知
                    _discovery_manage->onNotify(msg->method(), msg->host());
                    // 注册完了之后还需要进行注册消息应答
                    return registerResponse(conn, msg);
                }
                else if(opty == ServiceRequestOpType::SERVICE_DISCOVERY)
                {
                    LOG(DEBUGLEVEL, "进入到发现服务主机中!");
                    _discovery_manage->addDiscovery(conn, msg->method());
                    return discoveryResponse(conn, msg);
                }
                else
                {
                    // 请求服务未知
                    LOG(DEBUGLEVEL, "请求服务未知");
                    errorResponse(conn, msg);
                    return;
                }
            }

            void shutDown(const BaseConnection::ptr& conn)
            {
                // 首先判断这个连接是服务提供的连接还是发现者的连接
                auto it = _provide_manage->selectProvider(conn);
                if(it != nullptr)
                {
                    // 这是一个服务提供者下线了 需要发送下线通知
                    for(auto& e : it->methods)
                    {
                        // 给这个提供者提供的每一个方法都发送下线通知
                        _discovery_manage->offNotify(e, it->host);
                    }
                    // 最后再关闭连接
                    _discovery_manage->delDiscovery(conn);
                }
                // 如果他是服务发现者也关闭他的所有发现
                _discovery_manage->delDiscovery(conn);
            }
        private:
            void registerResponse(const BaseConnection::ptr& conn, const ServiceRequest::ptr& msg)
            {
                auto rsp = MessageFactory::create(MType::RSP_SERVICE);
                auto rpc_rsp = std::dynamic_pointer_cast<ServiceResponse>(rsp);
                rpc_rsp->setMethod(msg->method());
                rpc_rsp->setMType(MType::RSP_SERVICE);
                rpc_rsp->setOptype(ServiceRequestOpType::SERVICE_REGISTRY);
                rpc_rsp->setRcode(RCode::RCODE_OK);
                rpc_rsp->setId(msg->id());
                conn->send(rsp);
            }
            void discoveryResponse(const BaseConnection::ptr& conn, const ServiceRequest::ptr& msg)
            {
                auto rsp = MessageFactory::create(MType::RSP_SERVICE);
                auto rpc_rsp = std::dynamic_pointer_cast<ServiceResponse>(rsp);
                rpc_rsp->setMethod(msg->method());
                rpc_rsp->setMType(MType::RSP_SERVICE);
                rpc_rsp->setOptype(ServiceRequestOpType::SERVICE_DISCOVERY);
                rpc_rsp->setRcode(RCode::RCODE_OK);
                rpc_rsp->setId(msg->id());
                rpc_rsp->setHosts(_provide_manage->getHosts(msg->method()));
                conn->send(rsp);
            }
            void errorResponse(const BaseConnection::ptr& conn, const ServiceRequest::ptr& msg)
            {
                auto rsp = MessageFactory::create(MType::RSP_SERVICE);
                auto rpc_rsp = std::dynamic_pointer_cast<ServiceResponse>(rsp);
                rpc_rsp->setMethod(msg->method());
                rpc_rsp->setMType(MType::RSP_SERVICE);
                rpc_rsp->setOptype(ServiceRequestOpType::SERVICE_UNKONW);
                rpc_rsp->setRcode(RCode::RCODE_NOT_FOUND_SERVICE);
                rpc_rsp->setId(msg->id());
                rpc_rsp->setHosts(_provide_manage->getHosts(msg->method()));
                conn->send(rsp);
            }
        private:
            ProviderManage::ptr _provide_manage;
            DiscoveryManage::ptr _discovery_manage;
        };
    }
}